/* Nate Deisinger (ndeisinger@wisc.edu)
 *
 * This file is part of soot-csi, a project to apply lightweight
 * instrumentation to Java programs to facilitate debugging through
 * the Soot analysis framework.
 *
 * Copyright (c) 2014, Nate Deisinger, University of Wisconsin-Madison
 *   Madison, Wisconsin 53715
 *   All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor,
 *   Boston, MA  02110-1301
 *   USA
 * or retrieve version 2.1 at their website:
 *   http://www.gnu.org/licenses/lgpl-2.1.html
 */

package uwmadison.soot;

import java.util.Map;
import soot.*;
import soot.toolkits.graph.*;
import uwmadison.soot.analysis.*;

/**
 * Main class for Soot-CSI, an extension to the Soot Java instrumentation framework 
 * which applies instrumentation and outputs metadata to match the call coverage and 
 * path profiling described in Ohmann and Liblit, '14.
 * 
 * Can be run either as an Eclipse project for the Soot plug-in or standalone.
 * Standalone requires use of additional command-line arguments:
 * 
 * -profilelevel: Set to PT for path tracing only, CC for call coverage only, or full for both.  Default value is both.
 * -metadatadir: Directory to dump metadata to.  Defaults to current directory (see outDir variable.)
 * 
 * The program also requires whole-program analysis mode for Soot (-w) to be enabled.
 * 
 * @author Nate Deisinger
 */
public class SootCSI {

    public static boolean doPT = false;
    public static boolean doCC = false;

    public static String outDir = null;

    public static void main(String[] args) {

        // We force --keep-line-number due to a bug in Eclipse plugin
        // which does not carry that value over.
        String[] newArgs = new String[args.length + 1];
        for (int i = 0; i < args.length; i++)
        {
            newArgs[i+1] = args[i];
        }
        newArgs[0] = "--keep-line-number";
        

        // Perform option parsing
        boolean foundWholeProgram = false;
        boolean foundProfileLevel = false;
        boolean foundOutputDir = false;
        for (int i = 0; i < args.length; i++)
        {
            if (args[i].equals("-profilelevel"))
            {
                if (i == (args.length - 1))
                {
                    G.v().out.println("Error: no option specified for profiling level");
                    return;
                }
                else
                {
                    String profileLevel = args[i+1];
                    if (profileLevel.equalsIgnoreCase("pt"))
                    {
                        doPT = true;
                    }
                    else if (profileLevel.equalsIgnoreCase("cc"))
                    {
                        doCC = true;
                    }
                    else if (profileLevel.equalsIgnoreCase("full"))
                    {
                        doPT = true;
                        doCC = true;
                    }
                    else
                    {
                        G.v().out.println("Error: must specify cc, pt, or full as profiling level");
                        return;
                    }
                    foundProfileLevel = true;
                }
            }
            if( args[i].equalsIgnoreCase("-w") || args[i].equalsIgnoreCase("-whole-program")
                    || args[i].equalsIgnoreCase("--w") || args[i].equalsIgnoreCase("--whole-program"))
            {
                foundWholeProgram = true;
            }
            if(args[i].equalsIgnoreCase("-metadatadir"))
            {
                if (i == (args.length - 1))
                {
                    G.v().out.println("Error: no option specified for metadata output dir");
                    return;
                }
                outDir = args[i+1];
            }
            foundOutputDir = true;
        }

        if (!foundWholeProgram)
        {
            G.v().out.println("Error: whole-program mode not specified.  Exiting.");
            return;
        }

        if (!foundProfileLevel)
        {
            G.v().out.println("Warning: no profiling level found.  Assuming full.");

            //If using Eclipse, edit these lines to determine types of instrumentation.
            doPT = true;
            doCC = true;            
        }

        if (!foundOutputDir)
        {
            G.v().out.println("Warning: no output dir found.  Assuming current working dir.");

            //If using Eclipse, edit this line to determine metadata output directory.
            outDir = "";
        }

        PackManager.v().getPack("wjtp").add(
                new Transform("wjtp.CSISetup", new SceneTransformer() {

                    @Override
                    protected void internalTransform(String phaseName,
                            Map options) { 
                        Scene.v().addBasicClass("uwmadison.soot.analysis.PathTrace", SootClass.BODIES);
                        Scene.v().forceResolve("uwmadison.soot.analysis.PathTrace", SootClass.BODIES);
                        SootClass pathTraceClass = Scene.v().loadClassAndSupport("uwmadison.soot.analysis.PathTrace");
                        pathTraceClass.setApplicationClass();
                       
                        Scene.v().addBasicClass("uwmadison.soot.analysis.CallTrace", SootClass.BODIES);
                        Scene.v().forceResolve("uwmadison.soot.analysis.CallTrace", SootClass.BODIES);
                        SootClass CallTraceClass = Scene.v().loadClassAndSupport("uwmadison.soot.analysis.CallTrace");
                        CallTraceClass.setApplicationClass();
                     }

                }));
        PackManager.v().getPack("jtp").add(
                new Transform("jtp.AnalysisTransform", new BodyTransformer() {

                    protected void internalTransform(Body body, String phase, Map options) {
                        G.v().out.println(body.getMethod());
                        if(doPT)
                        {
                            new PathAnalysis(new ZonedBlockGraph(body));
                        }
                        if(doCC)
                        {
                            new CallAnalysis(new ExceptionalBlockGraph(body));
                        }
                    }
                }));
        /*SootClass pathTraceClass = Scene.v().loadClassAndSupport("uwmadison.soot.analysis.PathTrace");
        pathTraceClass.setApplicationClass();
        Scene.v().addBasicClass("uwmadison.soot.analysis.PathTrace", SootClass.SIGNATURES);
        Scene.v().forceResolve("uwmadison.soot.analysis.PathTrace", SootClass.SIGNATURES);

        SootClass CallTraceClass = Scene.v().loadClassAndSupport("uwmadison.soot.analysis.CallTrace");
        CallTraceClass.setApplicationClass();
        Scene.v().forceResolve("uwmadison.soot.analysis.CallTrace", SootClass.SIGNATURES);*/

        soot.Main.main(newArgs);
    }

}